module nade.controller;

import derelict.sdl.sdl;

import nade.array;


private Controller[]	_controllers;

Controller addController(Controller controller) { _controllers ~= controller; return controller; }
Controller[] allControllers() { return _controllers; }


class Controller {
	alias void delegate() DEvent;
	alias void delegate(real x, real y, real dx, real dy) DAbsoluteEvent;
	alias void delegate(real x, real y) DRelativeEvent;

	abstract bool processEvent(SDL_Event event);

	bool isRelative() { return false; }
	bool isAbsolute() { return false; }

	protected {
		real		_actionX, _actionY;
	}
}
alias Controller.DEvent DEvent;
alias Controller.DAbsoluteEvent DAbsoluteEvent;
alias Controller.DRelativeEvent DRelativeEvent;


class KeyboardController : Controller {
	override bool processEvent(SDL_Event event)
	{
		switch(event.type)
		{
			case SDL_KEYDOWN:
			{
				auto bindings = _pbindings[event.key.keysym.sym];
				foreach(dg; bindings) dg();
				return (bindings.length > 0);
			}
			case SDL_KEYUP:
			{
				auto bindings = _rbindings[event.key.keysym.sym];
				foreach(dg; bindings) dg();
				return (bindings.length > 0);
			}

			default:
		}

		return false;
	}

	void bind(SDLKey key, DEvent pevent, DEvent revent = null)
	{
		if(pevent !is null) _pbindings[key] ~= pevent;
		if(revent !is null) _rbindings[key] ~= revent;
	}

	void unbind(SDLKey key, DEvent pevent, DEvent revent = null)
	{
		if(pevent !is null) _pbindings[key].remove(pevent);
		if(revent !is null) _rbindings[key].remove(revent);
	}

	private {
		DEvent[][SDLK_LAST]		_pbindings;
		DEvent[][SDLK_LAST]		_rbindings;
	}
}


class JoystickController : Controller {
	this(SDL_Joystick* joystick)
	{
		assert(joystick !is null);
		_joystick = joystick;
		_pbindings.length = _rbindings.length = SDL_JoystickNumButtons(_joystick);
	}

	override bool processEvent(SDL_Event event)
	{
		switch(event.type)
		{
			case SDL_JOYBUTTONDOWN:
			{
				if(event.jbutton.which == SDL_JoystickIndex(_joystick))
				{
					auto bindings = _pbindings[event.jbutton.button];
					foreach(dg; bindings) dg();
					return (bindings.length > 0);
				}
				break;
			}
			case SDL_JOYBUTTONUP:
			{
				if(event.jbutton.which == SDL_JoystickIndex(_joystick))
				{
					auto bindings = _rbindings[event.jbutton.button];
					foreach(dg; bindings) dg();
					return (bindings.length > 0);
				}
				break;
			}
			case SDL_JOYAXISMOTION:
			{
				if(_abinding !is null  &&  event.jaxis.which == SDL_JoystickIndex(_joystick))
				{
					const jx = SDL_JoystickGetAxis(_joystick, 0);
					const jy = SDL_JoystickGetAxis(_joystick, 1);
					const real x = (jx - 0.5)/32768.0;
					const real y = (jy - 0.5)/32768.0;

					foreach(dg; _abinding) dg(x, y);

					return (_abinding.length > 0);
				}
			}

			default:
		}

		return false;
	}

	override bool isRelative() { return true; }

	void bind(ubyte index, DEvent pevent, DEvent revent = null)
	{
		if(pevent !is null) _pbindings[index] ~= pevent;
		if(revent !is null) _rbindings[index] ~= revent;
	}
	void bindAxis(DRelativeEvent event)
	{
		if(event !is null) _abinding ~= event;
	}

	uint buttonCount() { return _pbindings.length; }

	private {
		SDL_Joystick*		_joystick;
		DEvent[][]			_pbindings;
		DEvent[][]			_rbindings;
		DRelativeEvent[]	_abinding;
	}
}


class MouseController : Controller {
	this()
	{
	}

	override bool processEvent(SDL_Event event)
	{
		switch(event.type)
		{
			case SDL_MOUSEBUTTONDOWN:
			{
				if(event.button.button >= 1  &&  event.button.button <= 5)
				{
					auto bindings = _pbindings[event.button.button - 1];
					foreach(dg; bindings) dg();
					return (bindings.length > 0);
				}
				break;
			}
			case SDL_MOUSEBUTTONUP:
			{
				if(event.button.button >= 1  &&  event.button.button <= 5)
				{
					auto bindings = _rbindings[event.button.button - 1];
					foreach(dg; bindings) dg();
					return (bindings.length > 0);
				}
				break;
			}
			case SDL_MOUSEMOTION:
			{
				if(_mbinding !is null)
				{
					const x = (event.motion.x - 512)/512.0;
					const y = (384 - event.motion.y)/512.0;
					const dx = event.motion.xrel/512.0;
					const dy = -event.motion.yrel/512.0;

					foreach(dg; _mbinding) dg(x, y, dx, dy);

					return (_mbinding.length > 0);
				}
			}

			default:
		}

		return false;
	}

	override bool isAbsolute() { return true; }

	void bind(uint button, DEvent pevent, DEvent revent = null)
	{
		if(pevent !is null) _pbindings[button - 1] ~= pevent;
		if(revent !is null) _rbindings[button - 1] ~= revent;
	}
	void bindWheelUp(DEvent event)
	{
		bind(SDL_BUTTON_WHEELUP, event, null);
	}
	void bindWheelDown(DEvent event)
	{
		bind(SDL_BUTTON_WHEELDOWN, event, null);
	}
	void bindMove(DAbsoluteEvent event)
	{
		if(event !is null) _mbinding ~= event;
	}

	uint buttonCount() { return _pbindings.length; }

	private {
		DEvent[][5]			_pbindings;
		DEvent[][5]			_rbindings;
		DAbsoluteEvent[]	_mbinding;
	}
}
